package com.tabjin.rfidsocket.authority.shiro;

import com.tabjin.rfidsocket.common.RequestHolder;
import com.tabjin.rfidsocket.pojo.SysUser;
import com.tabjin.rfidsocket.pojo.dto.AclModuleLevelDTO;
import com.tabjin.rfidsocket.pojo.vo.SysAclVO;
import com.tabjin.rfidsocket.pojo.vo.SysRoleVO;
import com.tabjin.rfidsocket.pojo.vo.SysUserVO;
import com.tabjin.rfidsocket.service.sys.*;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 * @author Tabjin
 * create at 2021/2/26 14:57
 * @program emos-wx-api
 * @description Shiro 执行认证和授权的 realm 类
 */
@Component
public class OAuth2Realm extends AuthorizingRealm {
    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private SysUserService sysUserService;

    @Autowired
    private SysCoreService sysCoreService;

    @Autowired
    private SysRoleService sysRoleService;

    @Autowired
    private ThreadLocalToken threadLocalToken;

    /**
     * 令牌对象校验
     *
     * @param token AuthenticationToken 的子类对象
     * @return
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        boolean b = token instanceof OAuth2Token;// OAuth2Token 类型可以使用
        return b;
    }

    /**
     * 授权（验证权限时调用）
     *
     * @param collection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection collection) {
        SysUser sysUser = (SysUser) collection.getPrimaryPrincipal();// 至于能拿到用户信息是因为认证对象中传入了三个参数new SimpleAuthenticationInfo(sysUser, accessToken, getName())
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();

        Integer userId = sysUser.getId();

        // 根据用户名查询当前用户拥有的角色
        List<SysRoleVO> roleList = sysRoleService.getRoleListByUserId(userId);
        Set<String> roleNames = new HashSet<String>();
        for (SysRoleVO role : roleList) {
            roleNames.add(role.getName().toString());
        }
        // 将角色名称提供给info
        authorizationInfo.setRoles(roleNames);

        // 根据用户查询当前用户权限
        // 查询用户的权限列表
        List<SysAclVO> userAclList = sysCoreService.getUserAclList(userId);
        Set<SysAclVO> userAclSet = new HashSet<>(userAclList);
        Set<String> userAclUrlSet = new HashSet<>();
        Iterator it = userAclList.iterator();
        while (it.hasNext()) {
            SysAclVO next = (SysAclVO) it.next();
            userAclUrlSet.add(next.getUrl());
        }

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        // TODO 把用户列表添加到 info 对象中
        info.setStringPermissions(userAclUrlSet);
        return info;
    }

    /**
     * 认证（验证登录时调用）
     *
     * @param token 多态形式的令牌对象
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        // 从token令牌中获取 userId ，然后检测用户是否被冻结
        String accessToken = (String) token.getPrincipal();
        Integer userId = jwtUtil.getUserId(accessToken);
        // 根据userId获取用户
        SysUser sysUser = sysUserService.searchUserById(userId);
        if (sysUser == null) {
            throw new LockedAccountException("账号已被锁定，请联系管理员");
        }
        // TODO 往ThreadLocal中添加了用户对象
        SysUserVO sysUserVO = new SysUserVO();
        BeanUtils.copyProperties(sysUser, sysUserVO);
        RequestHolder.add(sysUserVO);
        SysUserVO currentUser = RequestHolder.getCurrentUser();
        threadLocalToken.setToken(accessToken);
        // 往 info 认证对象中添加用户信息、Token 字符串
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(sysUser, accessToken, getName());
        return info;
    }
}
